#define LOGGING 1

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "netcdf.h"
#include "HY1C_out.h"
#include "l12_proto.h"
#include "l12_parms.h"
#include "h5io.h"
#include "config.h"

char datestr[16] = {0};
//#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:        the pointer to the member.
 * @type:       the type of the container struct this is embedded in.
 * @member:     the name of the member within the struct.
 *
 */
#define container_of(ptr, type, offset) ({                      \
        (type *)( (char *)ptr - offset);})

/**
 * entry      - get the struct for this entry
 * @ptr:        the &struct list_head pointer.
 * @type:       the type of the struct this is embedded in.
 * @member:     the name of the list_struct within the struct.
 */
#define entry(ptr, offset) \
    ((char *)ptr + offset)


#define CHECK_ERR(content, e) \
    do {    \
        if (e != 0) { \
            LOG_ERR("Error:%s %s\n", content, nc_strerror(e));    \
        }    \
    } while (0);

enum data_type
{
    DT_INT,
    DT_FLOAT,
    DT_FLOAT_MULTI,
    DT_STRING
};

struct h5_read_item_t
{
    int sensor;
    int method;
    char * group_name;
    char * dataset;
    size_t offset;
    int    type;
    int invalid_flag;
    char * other_param; /* slope=slope,intercept=offset */
    void * (*cb)(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
};

/**
 * @brief -the callback function when get the latitude data
 *        should set the npix & nscan with this.
 * @param self - the h5_read_item_t item which invoked the callback
 * @param p - the l2rec + offset(dataset)
 * @param nscan
 * @param npix
 *
 * @return void *
 */
static void * range_beginning_date_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * range_beginning_time_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * navi_lat_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * navi_lon_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * geo_dataset_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * modis_lt_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);
static void * modis_lw_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param);

// another keep value of invalid is 0x40000000 which indicate that the gain is filtered by standard deviation.
// another keep value of invalid is 0x80000000 which indicate that the La is invalid
static struct h5_read_item_t hy1c_l2_items[] = {
    //	{SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "GlobalAttribute",    	   "matched_area",       offsetof(l2rec_HY1C, matched_area),       DT_FLOAT,       0x00000000, NULL, NULL},
    //{SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "GlobalAttribute",    	   "Range Beginning Date",offsetof(l2rec_HY1C, begin_date),        DT_STRING,      0x00000000, NULL, range_beginning_date_cb},
    //	{SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "GlobalAttribute",    "Range Beginning Time",offsetof(l2rec_HY1C, begin_time),        DT_STRING,      0x00000000, NULL, range_beginning_time_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Navigation Data",	       "Latitude",           offsetof(l2rec_HY1C, lat),                DT_FLOAT,       0x10000000, NULL, navi_lat_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Navigation Data",	       "Longitude",          offsetof(l2rec_HY1C, lon),                DT_FLOAT,       0x20000000, NULL, navi_lon_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC,             "Navigation Data",	       "modis_matched_flag", offsetof(l2rec_HY1C, modis_matched_flag), DT_INT,         0x00000000, NULL, NULL},
    {               SENSOR_CZI             , DPG_TRBMCC | DPG_TRBMC, "Navigation Data",	       "side_flag",          offsetof(l2rec_HY1C, side_flag),          DT_INT,         0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Scan-Line Attributes",   "Year",               offsetof(l2rec_HY1C, year),               DT_INT,         0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Scan-Line Attributes",   "Day",                offsetof(l2rec_HY1C, day),                DT_INT,         0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Scan-Line Attributes",   "Millisecond",        offsetof(l2rec_HY1C, msec),               DT_INT,         0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Sensor Band Parameters", "F0",                 offsetof(l2rec_HY1C, Fo),                 DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI,              DPG_TRBMC, "Field Data", 		       "AOT",                offsetof(l2rec_HY1C, field_AOT),          DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI,              DPG_TRBMC, "Field Data",             "Lw",                 offsetof(l2rec_HY1C, field_Lw),           DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI,              DPG_TRBMC, "Field Data",             "AOT_lonlat",         offsetof(l2rec_HY1C, field_AOT_lonlat),   DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI,              DPG_TRBMC, "Field Data",             "Lw_lonlat",          offsetof(l2rec_HY1C, field_Lw_lonlat),    DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI,              DPG_TRBMC, "Field Data",             "aer_model_num",      offsetof(l2rec_HY1C, field_aer_modnum),   DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "Lr",                 offsetof(l2rec_HY1C, Lr),                 DT_FLOAT_MULTI, 0x00000001, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "TLg",                offsetof(l2rec_HY1C, TLg),                DT_FLOAT_MULTI, 0x00000002, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "tLf",                offsetof(l2rec_HY1C, tLf),                DT_FLOAT_MULTI, 0x00000004, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "Lt",                 offsetof(l2rec_HY1C, Lt),                 DT_FLOAT_MULTI, 0x00000008, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "tg_sen",             offsetof(l2rec_HY1C, tg_sen),             DT_FLOAT_MULTI, 0x00000010, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "tg_sol",             offsetof(l2rec_HY1C, tg_sol),             DT_FLOAT_MULTI, 0x00000020, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "t_sen",              offsetof(l2rec_HY1C, t_sen),              DT_FLOAT_MULTI, 0x00000040, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "t_sol",              offsetof(l2rec_HY1C, t_sol),              DT_FLOAT_MULTI, 0x00000080, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "sena",               offsetof(l2rec_HY1C, sena),               DT_FLOAT,       0x00000100, "slope=Slope,intercept=Offset", geo_dataset_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "senz",               offsetof(l2rec_HY1C, senz),               DT_FLOAT,       0x00000200, "slope=Slope,intercept=Offset", geo_dataset_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "sola",               offsetof(l2rec_HY1C, sola),               DT_FLOAT,       0x00000400, "slope=Slope,intercept=Offset", geo_dataset_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "solz",               offsetof(l2rec_HY1C, solz),               DT_FLOAT,       0x00000800, "slope=Slope,intercept=Offset", geo_dataset_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "water_vapor",        offsetof(l2rec_HY1C, wv),                 DT_FLOAT,       0x00001000, "slope=Slope,intercept=Offset", geo_dataset_cb},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "l2_flags",           offsetof(l2rec_HY1C, flags),              DT_INT,         0x00000000, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS | SENSOR_CZI | SENSOR_UVI, DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "windspeed",          offsetof(l2rec_HY1C, ws),                 DT_FLOAT,       0x00000000, NULL, NULL},
    {SENSOR_COCTS,                           DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "pressure",           offsetof(l2rec_HY1C, pr),                 DT_FLOAT,       0x00000000, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS,                           DPG_TRBMCC | DPG_TRBMC, "Geophysical Data",       "humidity",           offsetof(l2rec_HY1C, rh),                 DT_FLOAT,       0x00000000, "slope=Slope,intercept=Offset", NULL},
    {SENSOR_COCTS,                           DPG_TRBMCC | DPG_TRBMC, "Sensor Band Parameters", "Tau_r",              offsetof(l2rec_HY1C, Tau_r),              DT_FLOAT,       0x00000000, NULL, NULL}
};

static struct h5_read_item_t modis_l2_items[] = {
    {0, 0, "navigation_data",      "latitude",      offsetof(l2rec_HY1C, lat),       DT_FLOAT,       0x00000000, NULL, navi_lat_cb},
    {0, 0, "navigation_data",      "longitude",     offsetof(l2rec_HY1C, lon),       DT_FLOAT,       0x00000000, NULL, navi_lon_cb},
    {0, 0, "scan_line_attributes", "year",          offsetof(l2rec_HY1C, year),      DT_INT,         0x00000000, NULL, NULL},
    {0, 0, "scan_line_attributes", "day",           offsetof(l2rec_HY1C, day),       DT_INT,         0x00000000, NULL, NULL},
    {0, 0, "scan_line_attributes", "msec",          offsetof(l2rec_HY1C, msec),      DT_INT,         0x00000000, NULL, NULL},
    {0, 0, "geophysical_data",     "Lt",            offsetof(l2rec_HY1C, Lt),        DT_FLOAT_MULTI, 0x00000000, "slope=scale_factor,intercept=add_offset", modis_lt_cb},
    {0, 0, "geophysical_data",     "Lw",            offsetof(l2rec_HY1C, Lw),        DT_FLOAT_MULTI, 0x00000000, "slope=scale_factor,intercept=add_offset", modis_lw_cb},
    {0, 0, "geophysical_data",     "aot",           offsetof(l2rec_HY1C, taua),      DT_FLOAT_MULTI, 0x00100000, "slope=scale_factor,intercept=add_offset", NULL},
    {0, 0, "geophysical_data",     "aer_model_max", offsetof(l2rec_HY1C, aermodmax), DT_INT,         0x00200000, NULL, geo_dataset_cb},
    {0, 0, "geophysical_data",     "aer_model_min", offsetof(l2rec_HY1C, aermodmin), DT_INT,         0x00400000, NULL, geo_dataset_cb},
    {0, 0, "geophysical_data",     "l2_flags",      offsetof(l2rec_HY1C, flags),     DT_INT,         0x00000000, NULL, NULL}
};

static char *strstrip(char *s)
{
    size_t size;
    char *end;

    size = strlen(s);

    if (!size)
        return s;

    end = s + size - 1;
    while (end >= s && isspace(*end))
        end--;
    *(end + 1) = '\0';

    while (*s && isspace(*s))
        s++;

    return s;
}

static void * range_beginning_date_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    self = self;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    //int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    l2rec->year = (int*)malloc(sizeof(int));
    l2rec->day = (int*)malloc(sizeof(int));

    char tmp[8];
    *(l2rec->year) = atoi(strncat(tmp, *(char**)p, 4));
    memset(tmp, 0, 8);
    *(l2rec->day) = atoi(strcat(tmp, *(char**)p + 9));
    return NULL;
}

static void * range_beginning_time_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    return NULL;
}

/**
 * @brief -the callback function when get the latitude data
 *        should set the npix & nscan with this.
 * @param self - the h5_read_item_t item which invoked the callback
 * @param p - the l2rec + offset(dataset)
 * @param nscan
 * @param npix
 *
 * @return void *
 */
static void * navi_lat_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    self = self;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    l2rec->npix = npix;
    l2rec->nscans = nscan;
    int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    if (!(l2rec->invalid)) {
        size_t invalid_size = (size_t)(nscan * npix * nwave) * sizeof(int32_t);
        l2rec->invalid = (int*)malloc(invalid_size);
        if (NULL == l2rec->invalid) {
            LOG_ERR("malloc invalid failed.\n");
            return NULL;
        }
        memset(l2rec->invalid, 0x00, invalid_size);
    }


    if (other_param != NULL) {
        float * matched_area = other_param;
        //float min_lon = matched_area[0];
        float min_lat = matched_area[1];
        //float max_lon = matched_area[2];
        float max_lat = matched_area[3];

        if (l2rec->type != 1) {
            int i = 0, j = 0;
            float **q = p;
            for (i = 0; i < nscan * npix; i++) {
                if (((*q)[i] <= min_lat) || ((*q)[i] >= max_lat)) {
                    for (j = 0; j < nwave; j++)
                        l2rec->invalid[i * nwave + j] |= self->invalid_flag;
                }
            }
        }
    }

    // init Lt_theroy and gain, the reason why put it here because
    // this is the place initializes nscans and npix.
    if (l2rec->type != 1) {
        size_t mem_size = nscan * npix * sizeof(float);
        l2rec->delphi = malloc(mem_size);
        if (NULL == l2rec->delphi) {
            LOG_ERR("malloc delphi failed.\n");
            return NULL;
        }

        l2rec->aermodmin = malloc(mem_size);
        if (NULL == l2rec->aermodmin) {
            LOG_ERR("malloc aermodmin failed.\n");
            return NULL;
        }

        l2rec->aermodmax = malloc(mem_size);
        if (NULL == l2rec->aermodmax) {
            LOG_ERR("malloc aermodmax failed.\n");
            return NULL;
        }

        l2rec->aerratio = malloc(mem_size);
        if (NULL == l2rec->aerratio) {
            LOG_ERR("malloc aerratio failed.\n");
            return NULL;
        }

        l2rec->eps = malloc(mem_size);
        if (NULL == l2rec->eps) {
            LOG_ERR("malloc eps failed.\n");
            return NULL;
        }

        mem_size = nscan * npix * nwave * sizeof(float);
        l2rec->Lt_theroy = malloc(mem_size);
        if (NULL == l2rec->Lt_theroy) {
            LOG_ERR("malloc Lt_theroy failed.\n");
            return NULL;
        }

        l2rec->La = malloc(mem_size);
        if (NULL == l2rec->La) {
            LOG_ERR("malloc La failed.\n");
            return NULL;
        }

        l2rec->gain = malloc(mem_size);
        if (NULL == l2rec->gain) {
            LOG_ERR("malloc gain failed.\n");
            return NULL;
        }

        l2rec->Lw = malloc(mem_size);
        if (NULL == l2rec->Lw) {
            LOG_ERR("malloc Lw failed.\n");
            return NULL;
        }

        l2rec->aot_new = malloc(mem_size);
        if (NULL == l2rec->aot_new) {
            LOG_ERR("malloc aot_new failed.\n");
            return NULL;
        }

        for (int i = 0; i < nscan * npix * nwave; i++) {
            l2rec->Lt_theroy[i] = BAD_FLT;
            l2rec->gain[i] = BAD_FLT;
        }
    }
    return NULL;
}

static void * navi_lon_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    self = self;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    if (!(l2rec->invalid)) {
        size_t invalid_size = (size_t)(nscan * npix * nwave) * sizeof(int32_t);
        l2rec->invalid = (char*)malloc(invalid_size);
        if (NULL == l2rec->invalid) {
            LOG_ERR("malloc invalid failed.\n");
            return NULL;
        }
        memset(l2rec->invalid, 0x00, invalid_size);
    }

    if (other_param != NULL) {
        float * matched_area = other_param;
        float min_lon = matched_area[0];
        //float min_lat = matched_area[1];
        float max_lon = matched_area[2];
        //float max_lat = matched_area[3];

        if (l2rec->type != 1) {
            int i = 0, j = 0;
            float **q = (float**)p;
            for (i = 0; i < nscan * npix; i++) {
                if (((*q)[i] <= min_lon) || ((*q)[i] >= max_lon)) {
                    for (j = 0; j < nwave; j++)
                        l2rec->invalid[i * nwave + j] |= self->invalid_flag;
                }
            }
        }
    }
    return NULL;
}

static void * geo_dataset_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    other_param = other_param;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    if (!(l2rec->invalid)) {
        size_t invalid_size = (size_t)(nscan * npix * nwave) * sizeof(int32_t);
        l2rec->invalid = (char*)malloc(invalid_size);
        if (NULL == l2rec->invalid) {
            LOG_ERR("malloc invalid failed.\n");
            return NULL;
        }
        memset(l2rec->invalid, 0x00, invalid_size);
    }

    int i = 0, j = 0;

    if (self->type == DT_INT) {
        int **q = (int **)p;
        for (i = 0; i < nscan * npix; i++) {
            if ((*q)[i] == BAD_INT) {
                for (j = 0; j < nwave; j++)
                    l2rec->invalid[i * nwave + j] |= self->invalid_flag;
            } else {
                for (j = 0; j < nwave; j++)
                    l2rec->invalid[i * nwave + j] |= 0;
            }
        }
    } else if (self->type == DT_FLOAT) {
        float **q = (float **)p;
        for (i = 0; i < nscan * npix; i++) {
            if ((*q)[i] == BAD_FLT) {
                for (j = 0; j < nwave; j++)
                    l2rec->invalid[i * nwave + j] |= self->invalid_flag;
            } else {
                for (j = 0; j < nwave; j++)
                    l2rec->invalid[i * nwave + j] |= 0;
            }
        }
    }
    return NULL;
}

static void * modis_lt_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    other_param = other_param;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    float **q = (float**)p;
    for (int i = 0; i < nscan * npix * nwave; i++)
    {
        if ((*q)[i] != BAD_FLT) {
            (*q)[i] = (*q)[i] * 0.1;
        }
    }

    return NULL;
}

static void * modis_lw_cb(struct h5_read_item_t *self, void * p, int nscan, int npix, void * other_param)
{
    other_param = other_param;
    l2rec_HY1C *l2rec = container_of(p, l2rec_HY1C, self->offset);
    int nwave = (l2rec->type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

    float **q = (float**)p;
    for (int i = 0; i < nscan * npix * nwave; i++)
    {
        if ((*q)[i] != BAD_FLT) {
            (*q)[i] = (*q)[i] * 0.1;
        }
    }

    return NULL;
}

static void H5_nc_get_row_col(const int group_ncid,
        const char* name,
        int *row,
        int *col)
{
    int varid = -1;
    int ret = 0;
    int dimsp = 0;
    int dimidsp[8] = { -1, -1 };
    *row = 0;
    *col = 0;

    char err_str[128] = {0};
    sprintf(err_str, "groupid %d name %s", group_ncid, name);
    ret = nc_inq_varid(group_ncid, name, &varid);
    CHECK_ERR(err_str, ret);
    ret = nc_inq_varndims(group_ncid, varid, &dimsp);
    CHECK_ERR(err_str, ret);
    ret = nc_inq_vardimid(group_ncid, varid, dimidsp);
    CHECK_ERR(err_str, ret);
    if (dimsp == 0) {
        *row = 0;
        *col = 0;
    }
    else if (dimsp == 1) {
        ret = nc_inq_dimlen(group_ncid, dimidsp[0], (size_t*)row);
        CHECK_ERR(err_str, ret);
        *col = 1;
    }
    else {
        size_t tmp_row = 0;
        size_t tmp_col = 0;
        ret = nc_inq_dimlen(group_ncid, dimidsp[0], (size_t*)&tmp_row);
        *row = tmp_row;
        CHECK_ERR(err_str, ret);
        ret = nc_inq_dimlen(group_ncid, dimidsp[1], (size_t*)&tmp_col);
        *col = tmp_col;
        CHECK_ERR(err_str, ret);
    }
}

static void H5_nc_get_var_float(const int group_ncid,
        const char *name,
        float ***var,
        int *nscan,
        int *npix,
        float *slope,
        float *intercept,
        const char * slope_str,
        const char * intercept_str)
{
    int size = 0;
    int varid = -1;
    int ret = 0;

    *slope = 1;
    *intercept = 0;

    H5_nc_get_row_col(group_ncid, name, nscan, npix);
    **var = NULL;
    size = (*nscan) * (*npix);
    if (size <= 0)
        return;

    char err_str[128] = {0};
    sprintf(err_str, "groupid %d %s", group_ncid, name);

    ret = nc_inq_varid(group_ncid, name, &varid);
    CHECK_ERR(err_str, ret);

    if ((slope_str != NULL) && (intercept_str != NULL)) {
        ret = nc_get_att_float(group_ncid, varid, slope_str, slope);
        sprintf(err_str, "groupid %d %s get slope failed", group_ncid, name);
        CHECK_ERR(err_str, ret);
        ret = nc_get_att_float(group_ncid, varid, intercept_str, intercept);
        sprintf(err_str, "groupid %d %s get intercept failed", group_ncid, name);
        CHECK_ERR(err_str, ret);
    }

    **var = (float *)malloc((size_t)size * sizeof(float));
    if (**var == NULL) {
        LOG_ERR("groupid %d name %s malloc failed.\n", group_ncid, name);
        return;
    }

    ret = nc_get_var_float(group_ncid, varid, **var);

    LOG_DBG("groupid %d name %s nscan %d npix %d slope = %f intercept = %f\n",
            group_ncid, name, *nscan, *npix, *slope, *intercept);
    return;
}

static void H5_nc_get_var_int(int group_ncid,
        const char *name,
        int ***var,
        int *nscan,
        int *npix,
        float *slope,
        float *intercept,
        const char * slope_str,
        const char * intercept_str)
{
    int size = 0;
    int varid = -1;
    int ret = 0;

    *slope = 1;
    *intercept = 0;

    H5_nc_get_row_col(group_ncid, name, nscan, npix);
    **var = NULL;
    size = (*nscan) * (*npix);
    if (size <= 0)
        return;

    char err_str[128] = {0};
    sprintf(err_str, "groupid %d %s", group_ncid, name);

    ret = nc_inq_varid(group_ncid, name, &varid);
    CHECK_ERR(err_str, ret);

    if ((slope_str != NULL) && (intercept_str != NULL)) {
        ret = nc_get_att_float(group_ncid, varid, slope_str, slope);
        CHECK_ERR(err_str, ret);
        ret = nc_get_att_float(group_ncid, varid, intercept_str, intercept);
        CHECK_ERR(err_str, ret);
    }

    **var = (int *)malloc((size_t)size * sizeof(int));
    ret = nc_get_var_int(group_ncid, varid, **var);

    LOG_DBG("groupid %d name %s nscan %d npix %d value %d\n",
            group_ncid, name, *nscan, *npix, ***var);
    return;
}

// The data is malloced in this function. should free.
static float* fill_l2_float_multi(const int grp_id,
        const char * name,
        float *** var,
        float * wave,
        int nwave,
        int *nscan,
        int *npix,
        int32_t * flags,
        int32_t invalid_flag,
        const char * slope_str,
        const char * intercept_str)
{
    int i= 0, j = 0, k = 0;

    float* tmp_data[MAX_WAVE] = {0};
    float slope[MAX_WAVE] = {0};
    float intercept[MAX_WAVE] = {0};

    char dsname[64] = {'\0'};
    for (i = 0; i < nwave; i++) {
        float **tmp = &(tmp_data[i]);
        snprintf(dsname, sizeof(dsname), "%s_%d", name, (int)(wave[i]));
        H5_nc_get_var_float(grp_id, dsname, &tmp, nscan, npix,
                &slope[i], &intercept[i], slope_str, intercept_str);
    }

    size_t trans_size = sizeof(float) * (size_t)(nwave * (*npix) * (*nscan));
    float *trans = (float *)malloc(trans_size);
    if ((NULL == trans) || (trans_size <= 0)) {
        LOG_ERR("malloc trans failed.\n");
        return NULL;
    }

    memset(trans, 0x00, trans_size);

    for (i = 0; i < (*nscan); i++) {
        for (j = 0; j < (*npix); j++) {
            for (k = 0; k < nwave; k++) {
                if (tmp_data[k] == NULL)
                    continue;

                if (tmp_data[k][i * (*npix) + j] == BAD_FLT) {
                    /* for TRBMC, no need to check modis Lw/aot. */
                    if ((g_cfg.DPG_method == DPG_TRBMC) &&
                            (!strncmp(name, "Lw", 2)  ||
                             !strncmp(name, "aot", 3))) {
                        continue;
                    }
                    flags[i * (*npix) * nwave + j * nwave + k] |= invalid_flag;
                    trans[i * (*npix) * nwave + j * nwave + k] = BAD_FLT;
                } else {
                    flags[i * (*npix) * nwave + j * nwave + k] |= 0;
                    trans[i * (*npix) * nwave + j * nwave + k] =
                        tmp_data[k][i * (*npix) + j] * slope[k] + intercept[k];
                }
            }
        }
    }

    for (i = 0; i < nwave; i++) {
        free(tmp_data[i]);
    }

    **var = trans;
    return trans;
}

/**
 * @brief read HY1C or MODIS file
 *
 * @param filename file-name
 * @param type 0 - HY1C
 *             1 - MODIS
 *
 * @return pointer to l2recHY1C, this memory is alloced in this function
 *         and its iterms also alloced in this function.
 */
l2rec_HY1C* read_H1C_l2_hdf5(char* filename, int type)
{
    if (NULL == filename) {
        LOG_ERR("File is Null.\n");
        return NULL;
    }

    /*****************************************************/
    int ret = -1;
    int read_area_result = -1;
    float matched_area[4] = {0};
    if (type != 1) {
        h5io_str fid;
        h5io_openr(filename, 0, &fid);
        read_area_result = h5io_rd_attr(&fid, "matched_area", matched_area);
        ret = h5io_rd_attr(&fid, "Range Beginning Date", datestr);
        h5io_close(&fid);
    }


    int npix = 0;
    int nscan = 0;

    float slope = 1;
    float intercept = 0;

    l2rec_HY1C *l2rec = (l2rec_HY1C*)malloc(sizeof(l2rec_HY1C));
    if (NULL == l2rec) {
        LOG_ERR("malloc l2rec failed.\n");
        goto fail_exist;
    }

    init_l2rec_HY1C(l2rec, type);

    //nc_set_log_level(7);
    int ncid = -1;
    if ((ret = nc_open(filename, NC_NOWRITE, &ncid)) != 0) {
        LOG_ERR("Error: open %s failed. %s\n", filename, nc_strerror(ret));
        goto fail_exist;
    }

    int ndims, nvars, ngatts, unlimdimid;
    if ((ret = nc_inq(ncid, &ndims, &nvars, &ngatts, &unlimdimid)) != 0) {
        LOG_ERR("Error: %s get attr failed. %s\n", filename, nc_strerror(ret));
        goto fail_exist;
    }

    size_t item_size = 0;
    struct h5_read_item_t *p = (type == 1) ? modis_l2_items : hy1c_l2_items;
    if (type == 1)
        item_size = sizeof(modis_l2_items) / sizeof(struct h5_read_item_t);
    else
        item_size = sizeof(hy1c_l2_items) / sizeof(struct h5_read_item_t);

    char * last_group = NULL;
    char other_param[512] = {0};
    int grp_id = -1;
    for (size_t i = 0; i < item_size; i++, p++) {
        if ((type != 1) && (p->sensor & g_cfg.sensor_id) == 0) {
            continue;
        }

        if ((type != 1) && (p->method & g_cfg.DPG_method) == 0) {
            continue;
        }

        if ((last_group == NULL) || (strcmp(p->group_name, last_group))) {
            last_group = p->group_name;

            if (!strcmp(p->group_name, "GlobalAttribute")) {
                read_global_attribute(p, ncid, l2rec);
                continue;
            }

            if ((ret = nc_inq_grp_ncid(ncid, p->group_name, &grp_id)) != 0) {
                LOG_ERR("Error: %s get %s failed. %s\n",
                        filename, p->group_name, nc_strerror(ret));
                goto fail_exist;
            }
        }

        char * slope_str = NULL;
        char * intercept_str = NULL;
        if (p->other_param) {
            size_t other_param_len = strlen(p->other_param) + 1;
            if (other_param_len > 512) {
                LOG_ERR("length of other_param %s %d is exceed.\n",
                        p->other_param, other_param_len);
                goto fail_exist;
            }
            snprintf(other_param, other_param_len, p->other_param);

            char *p = other_param;
            char *s = NULL;
            while ((s = strsep(&p, ",")) != NULL) {
                char * key = NULL;
                char * value = s;
                key = strsep(&value, "=");
                key = strstrip(key);
                value = strstrip(value);

                if (!strcmp(key, "slope"))
                    slope_str = value;
                else if (!strcmp(key, "intercept"))
                    intercept_str = value;
                else
                    LOG_WARN("invalid other_param %s=%s\n", key, value);
            }
        }

        void **q = (void**)entry(l2rec, p->offset);
        switch(p->type) {
            case (DT_STRING): {
                                  break;
                              }
            case (DT_INT): {
                               H5_nc_get_var_int(grp_id, p->dataset, (int ***)&q, &nscan, &npix,
                                       &slope, &intercept, slope_str, intercept_str);
                               for (int i = 0; i < nscan * npix; i++) {
                                   *((int*)q[0]+i) = *((int*)q[0]+i) * slope + intercept;
                               }
                           }
                           break;
            case (DT_FLOAT): {
                                 H5_nc_get_var_float(grp_id, p->dataset, (float ***)&q, &nscan, &npix,
                                         &slope, &intercept, slope_str, intercept_str);
                                 for (int i = 0; i < nscan * npix; i++) {
                                     *((float*)q[0]+i) = *((float*)q[0]+i) * slope + intercept;
                                 }
                             }
                             break;
            case (DT_FLOAT_MULTI): {
                                       float * wave = (type == 1) ? g_cfg.wave_modis : g_cfg.wave_hy1c;
                                       int nwave = (type == 1) ? g_cfg.modis_nwave : g_cfg.hy1c_nwave;

                                       fill_l2_float_multi(grp_id, p->dataset, (float ***)&q, wave, nwave,
                                               &nscan, &npix,
                                               l2rec->invalid, p->invalid_flag,
                                               slope_str, intercept_str);
                                   }
                                   break;
            default:
                                   break;
        }

        if (p->cb) {
            if (!strcmp(p->dataset, "Latitude") ||
                    !strcmp(p->dataset, "Longitude")) {
                if (read_area_result == 0)
                    p->cb(p, q, nscan, npix, matched_area);
                else
                    p->cb(p, q, nscan, npix, NULL);
            } else {
                p->cb(p, q, nscan, npix, NULL);
            }
        }
    }

    ret = nc_close(ncid);
    CHECK_ERR("close hdf5 failed", ret);
    return l2rec;

fail_exist:
    LOG_ERR("error occured.");
    if (l2rec)
        free(l2rec);
    return NULL;
}

int read_global_attribute(struct h5_read_item_t *p, int ncid, l2rec_HY1C *l2rec)
{
    int ret = 0;
    void **q = (void**)entry(l2rec, p->offset);

    switch(p->type) {
        case (DT_STRING): {
                              *q = (void *)malloc(sizeof(char) * 24);
                              memset(*q, 0, sizeof(char) * 24);
                              ret = nc_get_att_text(ncid, NC_GLOBAL, p->dataset, *q);
                              char *p = nc_strerror(ret);
                              LOG_ERR("%s", p);
                              break;
                          }
        case (DT_INT): {
                           break;
                       }
        case (DT_FLOAT): {
                             break;
                         }
        case (DT_FLOAT_MULTI): {
                                   break;
                               }
        default:
                               break;
    }

    if (p->cb) {
        p->cb(p, q, 0, 0, NULL);
    }
}
